package edu.study.common.util.file;

import edu.study.common.util.ftp.ScpBean;
import edu.study.common.util.ftp.SftpUtil;
import edu.study.common.util.system.OSinfo;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 * 文件工具类
 *
 * @author yanjiawei
 */
@Slf4j
public class FileTools {

    /**
     * 根据文件全名获取文件名称
     *
     * @param fileFullName
     * @return
     */
    public static String getFileName(String fileFullName) {
        return fileFullName.substring(fileFullName.lastIndexOf(File.separator) + 1);
    }

    /**
     * 根据文件全名获取文件所在路径
     *
     * @param fileFullName
     * @return
     */
    public static String getPath(String fileFullName) {
        return fileFullName.substring(0, fileFullName.lastIndexOf(File.separator) + 1);
    }

    /**
     * 转换分隔符
     *
     * @param fileFullName
     * @return
     */
    public static String convertSeparator(String fileFullName) {
        if (OSinfo.isWindows()) {
            fileFullName.replaceAll("//", File.separator);
        } else {
            fileFullName.replaceAll("\\\\", File.separator);
        }
        return fileFullName;
    }

    /**
     * 创建多级目录
     *
     * @param localFilePath
     */
    public static void mkdirs(String localFilePath) {
//        File file = new File(localFilePath.substring(0, localFilePath.lastIndexOf(File.separatorChar) + 1));
        File file = new File(localFilePath).getParentFile();
        log.info("FileTools.mkdirs(); file is {}", file);
        //如果目录不存在,则创建目录;
        if (!file.exists()) {
            //mkdirs是创建多级目录,而mkdir是创建单级目录;
            file.mkdirs();
        }
    }

    public static void touchFile(String fileFullPath) throws IOException {
        log.info("FileTools.createNewFile(); fileFullPath is {}", fileFullPath);
        File file = new File(fileFullPath);
        //如果文件不存在，则创建文件
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    /**
     * 复制文件
     *
     * @param sourceFile
     * @param targetFile
     * @throws IOException
     */
    public static void copyFile(String sourceFile, String targetFile) throws IOException {
        log.info("sourceFile:{} --> targetFile:{} 开始复制", sourceFile, targetFile);
        FileInputStream source = null;
        FileOutputStream target = null;
        try {
            File tmpTargetFile = new File(targetFile.substring(0, targetFile.lastIndexOf(File.separatorChar)));
            log.info("创建targetFile = {} File对象", tmpTargetFile.getAbsolutePath());
            if (!tmpTargetFile.exists()) {
                tmpTargetFile.mkdirs();
            }
            log.info("targetFile = {} File对象创建成功", tmpTargetFile.getAbsolutePath());
            source = new FileInputStream(new File(sourceFile));
            target = new FileOutputStream(new File(targetFile));
            byte[] b = new byte[1024 * 5];
            int data;
            while ((data = source.read(b)) != -1) {
                target.write(b, 0, data);
            }
            source.close();
            target.close();
        } finally {
            // 关闭流
            if (source != null)
                source.close();
            if (target != null)
                target.close();
        }
        log.info("sourceFile:{} --> targetFile:{} 复制完成", sourceFile, targetFile);
    }

    /**
     * 复制文件
     *
     * @param sourceFile
     * @param targetFile
     * @throws IOException
     */
    public static void copyFile(File sourceFile, File targetFile) throws IOException {
        BufferedInputStream inBuff = null;
        BufferedOutputStream outBuff = null;
        try {
            // 新建文件输入流并对它进行缓冲
            inBuff = new BufferedInputStream(new FileInputStream(sourceFile));
            // 新建文件输出流并对它进行缓冲
            outBuff = new BufferedOutputStream(new FileOutputStream(targetFile));
            // 缓冲数组
            byte[] b = new byte[1024 * 5];
            int len;
            while ((len = inBuff.read(b)) != -1) {
                outBuff.write(b, 0, len);
            }
            // 刷新此缓冲的输出流
            outBuff.flush();
        } finally {
            // 关闭流
            if (inBuff != null)
                inBuff.close();
            if (outBuff != null)
                outBuff.close();
        }
    }

    /**
     * 复制文件夹
     *
     * @param sourceDir
     * @param targetDir
     * @throws IOException
     */
    public static void copyDirectiory(String sourceDir, String targetDir) throws IOException {
        // 新建目标目录
        (new File(targetDir)).mkdirs();
        // 获取源文件夹当前下的文件或目录
        File[] file = (new File(sourceDir)).listFiles();
        for (int i = 0; i < file.length; i++) {
            if (file[i].isFile()) {
                // 源文件
                File sourceFile = file[i];
                // 目标文件
                File targetFile = new File(new File(targetDir).getAbsolutePath() + File.separator + file[i].getName());
                copyFile(sourceFile, targetFile);
            }
            if (file[i].isDirectory()) {
                // 准备复制的源文件夹
                String dir1 = sourceDir + "/" + file[i].getName();
                // 准备复制的目标文件夹
                String dir2 = targetDir + "/" + file[i].getName();
                copyDirectiory(dir1, dir2);
            }
        }
    }

    /**
     * sftp 方式上传文件
     *
     * @param localFilePath
     * @param remoteFilePath
     * @param scpBean
     * @return true & false
     * @throws IOException
     */
    public static boolean upLoadFileBySftp(String localFilePath, String remoteFilePath, ScpBean scpBean) throws IOException {
        log.info("start FileTools.upLoadFileBySftp localFilePath={},remoteFilePath={},scpBean={}", localFilePath, remoteFilePath, scpBean.toString());
        boolean result = false;
        try {
            log.info("FileTools.upLoadFile()  upload RemoteAddressAndRemotePort = {} : {}", scpBean.getRemoteAddress(), scpBean.getRemotePort());
            result = SftpUtil.upload(scpBean, remoteFilePath, localFilePath);
        } catch (Exception e) {
            log.error("FileTools.upLoadFileBySftp - error uploadFile()", e);
            throw new IOException("file upLoadFile exception :localFilePath=" + localFilePath + "remoteFilePath=" + remoteFilePath);
        }
        log.info("end FileTools.upLoadFileBySftp localFilePath= {},remoteFilePath= {},scpBean= {},result={}", localFilePath, remoteFilePath, scpBean.toString(), result);
        return result;
    }

    /**
     * @param remoteFilePath
     * @param localFilePath
     * @param scpBean
     * @return true & false
     * @throws IOException
     */
    public static boolean downLoadFileBySftp(String remoteFilePath, String localFilePath, ScpBean scpBean) throws IOException {
        log.info("start FileTools.downLoadFileBySftp localFilePath={},remoteFilePath={},scpBean={}", localFilePath, remoteFilePath, scpBean.toString());
        boolean result = false;
        try {
            log.info("FileTools.downLoadFileBySftp() RemoteAddressAndRemotePort = {}, : {}", scpBean.getRemoteAddress(), scpBean.getRemotePort());
            File file = new File(localFilePath.substring(0, localFilePath.lastIndexOf(File.separatorChar) + 1));
            log.info("FileTools.downLoadFileBySftp() file is {}", file);
            //如果目录不存在,则创建目录;  
            if (!file.exists()) {
                //mkdirs是创建多级目录,而mkdir是创建单级目录;  
                file.mkdirs();
            }
            result = SftpUtil.download(scpBean, remoteFilePath, localFilePath);
        } catch (Exception e) {
            log.error("FileTools.downLoadFileBySftp - error downLoadFile()", e);
            throw new IOException("file downLoadFileBySftp exception :localFilePath=" + localFilePath + "remoteFilePath=" + remoteFilePath);
        }
        log.info("end FileTools.downLoadFileBySftp localFilePath= {} ,remoteFilePath= {} , scpBean= {} ,result = {}", localFilePath, remoteFilePath, scpBean.toString(), result);
        return result;
    }

    /**
     * 对directory目录下的文件压缩，保存为指定的文件zipFile
     *
     * @param directory
     * @param zipFile
     */
    public static void zip(String directory, String zipFile) {
        try {
            ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));
            fileZip(zos, new File(directory));
            zos.close();
        } catch (Exception e) {
            log.error("压缩文件发生异常", e);
        }
    }

    /**
     * 压缩文件
     *
     * @param zos
     * @param file
     * @throws Exception
     */
    private static void fileZip(ZipOutputStream zos, File file) throws Exception {
        if (file.isFile()) {
            zos.putNextEntry(new ZipEntry(file.getName()));
            FileInputStream fis = new FileInputStream(file);
            byte[] bb = new byte[2048];
            int aa = 0;
            while ((aa = fis.read(bb)) != -1) {
                zos.write(bb, 0, aa);
            }
            fis.close();
            log.info("文件名为{}", file.getName());
        } else {
            directoryZip(zos, file, "");
        }
    }

    /**
     * 压缩文件
     *
     * @param zos
     * @param zipEntryName
     * @param bytes
     * @throws Exception
     */
    public static void byteArrayToZipFile(ZipOutputStream zos, String zipEntryName, byte[] bytes) {
        ByteArrayInputStream byteArrayInputStream = null;
        try {
            zos.putNextEntry(new ZipEntry(zipEntryName));
            byteArrayInputStream = new ByteArrayInputStream(bytes);
            byte[] bb = new byte[2048];
            int aa = 0;
            while ((aa = byteArrayInputStream.read(bb)) != -1) {
                zos.write(bb, 0, aa);
            }
            zos.closeEntry();
            byteArrayInputStream.close();
            log.info("文件名为{}", zipEntryName);
        } catch (Exception e) {
            log.warn("close byteArrayInputStream fail.try again!");
        } finally {
            try {
                if (byteArrayInputStream != null) {
                    byteArrayInputStream.close();
                }
            }catch (Exception e){
                log.warn("close byteArrayInputStream fail.");
            }
        }

    }


    /**
     * 解压缩文件
     * 适合zip文件中只有一个文件(txt)的case
     *
     * @param fileFullName
     * @throws Exception
     */
    public static String fileUnzip(String fileFullName) throws FileNotFoundException, IOException {
        ZipFile zfile = null;
        OutputStream os = null;
        InputStream is = null;

        try {
            zfile = new ZipFile(fileFullName);
            Enumeration<? extends ZipEntry> zList = zfile.entries();
            ZipEntry ze = null;
            byte[] buf = new byte[1024];
            if (zList.hasMoreElements()) {
                ze = (ZipEntry) zList.nextElement();
            }
            if (ze == null) {
                return null;
            }
            // 变换文件后缀为 "txt"
            String fileFullNameWithTxt = fileFullName.substring(0, fileFullName.lastIndexOf(".")) + ".txt";
            os = new BufferedOutputStream(new FileOutputStream(fileFullNameWithTxt));
            is = new BufferedInputStream(zfile.getInputStream(ze));
            int readLen = 0;
            while ((readLen = is.read(buf, 0, 1024)) != -1) {
                os.write(buf, 0, readLen);
            }
            return fileFullNameWithTxt;
        } finally {
            if (is != null) {
                is.close();
            }
            if (os != null) {
                os.close();
            }
            if (zfile != null) {
                zfile.close();
            }
        }
    }

    private static void directoryZip(ZipOutputStream out, File f, String base) throws Exception {
        // 如果传入的是目录
        if (f.isDirectory()) {
            File[] fl = f.listFiles();
            // 创建压缩的子目录
            out.putNextEntry(new ZipEntry(base + "/"));
            if (base.length() == 0) {
                base = "";
            } else {
                base = base + "/";
            }
            for (int i = 0; i < fl.length; i++) {
                directoryZip(out, fl[i], base + fl[i].getName());
            }
        } else {
            // 把压缩文件加入rar中
            out.putNextEntry(new ZipEntry(base));
            if (!f.exists()) {
                f.createNewFile();
            }
            FileInputStream in = new FileInputStream(f);
            byte[] bb = new byte[2048];
            int aa = 0;
            while ((aa = in.read(bb)) != -1) {
                out.write(bb, 0, aa);
            }
            in.close();
        }
    }

}
